package me.yricky.mayu.utils.sequence

import androidx.compose.runtime.mutableStateOf
import me.yricky.mayu.ui.fasta.FastaSeqModel
import me.yricky.mayu.utils.sequence.SeqAliJImpl.col
import me.yricky.mayu.utils.sequence.SeqAliJImpl.row

/**
 * @author Yricky
 * @date 2022/4/18
 */
class SequenceAlignmentSession(
    private val seq1: FastaSeqModel,
    private val seq2: FastaSeqModel,
    val pairScore:(Char,Char)->Int,
    val vacancyScore:Int,
    private val onFinish:()->Unit = {}
) {
    private val impl by lazy{
        SeqAliJImpl(
            seq1,
            seq2,
            { c1,c2 -> pairScore(c1,c2) },
            vacancyScore
        )
    }
    val result:Pair<Int,List<Pair<Int,Int>>> get() = Pair(impl.score,impl.resultIndexes.map { Pair(row(it),col(it)) })
    val progress = mutableStateOf(0f)
    val bestScore get() = impl.bestScore

    var lastTimeConsumeMillis = 0L
        private set

    fun run(){
        progress.value = 0f
        impl.progressNotifier = Runnable{
            progress.value = impl.progress
        }
        val t1 = System.currentTimeMillis()
        impl.run()
        lastTimeConsumeMillis = System.currentTimeMillis() - t1
        onFinish()
    }

    fun traceIterator(index:Pair<Int,Int>):Iterator<Pair<Int,Int>>{
        return object :Iterator<Pair<Int,Int>>{
            var rowIndex = index.first
            var colIndex = index.second
            var nxtIndex = getNextIndex()


            fun getNextIndex():Pair<Int,Int> {
                if(rowIndex <0 ||colIndex < 0){
                    return Pair(-2,-2)
                }
                val scoreLT = impl.get(rowIndex - 1,colIndex - 1, 0)
                val scoreT = impl.get(rowIndex - 1,colIndex, 0)
                val scoreL = impl.get(rowIndex,colIndex - 1, 0)
                return when (impl.get(rowIndex,colIndex, 0)) {
                    scoreLT + pairScore(seq1.seq[rowIndex],seq2.seq[colIndex]) -> {
                        Pair(rowIndex-1,colIndex-1)
                    }
                    scoreL + vacancyScore -> {
                        Pair(rowIndex,colIndex-1)
                    }
                    scoreT + vacancyScore -> {
                        Pair(rowIndex-1,colIndex)
                    }
                    else -> {
                        Pair(-2,-2)
                    }
                }
            }
            override fun hasNext(): Boolean {
                return nxtIndex != Pair(-2,-2)
            }

            override fun next(): Pair<Int, Int> {
                val ret = Pair(
                    if(nxtIndex.first == rowIndex-1)rowIndex else -1,
                    if(nxtIndex.second == colIndex-1)colIndex else -1,
                )
                rowIndex = nxtIndex.first
                colIndex = nxtIndex.second
                nxtIndex = getNextIndex()
                return ret
            }

        }
    }


    companion object{
        fun simpleMatchPairScore(matchScore:Int = 3, mismatchScore:Int = -3):(Char, Char)->Int = { c1, c2->
            if(c1 == c2){
                matchScore
            }else{
                mismatchScore
            }
        }
    }
}